local CombatCaps_Defaults = {
	["Options"] = {
		["LevelCap"] = 3
	}
};

COMBATCAPS_RATINGS = {
	[60] = {
		[CR_HIT_MELEE] = 9.37931,
		[CR_EXPERTISE] = 2.34483,
		[CR_HIT_SPELL] = 8,
	},
	[70] = {
		[CR_HIT_MELEE] = 14.7905,
		[CR_EXPERTISE] = 3.69761,
		[CR_HIT_SPELL] = 12.6154
	},
	[80] = {
		[CR_HIT_MELEE] = 30.7548,
		[CR_EXPERTISE] = 7.68869,
		[CR_HIT_SPELL] = 26.232
	},
	[81] = {
		[CR_HIT_MELEE] = 40.3836,
		[CR_EXPERTISE] = 10.0959,
		[CR_HIT_SPELL] = 34.4448
	},
	[82] = {
		[CR_HIT_MELEE] = 53.0304,
		[CR_EXPERTISE] = 13.2576,
		[CR_HIT_SPELL] = 45.2318
	},
	[83] = {
		[CR_HIT_MELEE] = 69.6653,
		[CR_EXPERTISE] = 17.4163,
		[CR_HIT_SPELL] = 59.4204
	},
	[84] = {
		[CR_HIT_MELEE] = 91.4738,
		[CR_EXPERTISE] = 22.8685,
		[CR_HIT_SPELL] = 78.0218
	},
	[85] = {
		[CR_HIT_MELEE] = 120.109,
		[CR_EXPERTISE] = 30.0272,
		[CR_HIT_SPELL] = 102.446
	}
};

COMBATCAPS_FORMATSTRING = "%s %s(%i)%s";

local addonName, L = ...; -- Let's use the private table passed to every .lua file to store our locale
local function defaultFunc(L, key)
 -- If this function was called, we have no localization for this key.
 -- We could complain loudly to allow localizers to see the error of their ways, 
 -- but, for now, just return the key as its own localization. This allows you to 
 -- avoid writing the default localization out explicitly.
 return key;
end
setmetatable(L, {__index=defaultFunc})

local function CombatCaps_Cap()
	return CombatCaps_CharacterConfig["Options"]["LevelCap"];
end

local function CombatCaps_GetCalculationModeName()
	local lc = CombatCaps_Cap(); 
	if ( lc == 2 ) then
		return LFG_TYPE_HEROIC_DUNGEON;
	elseif ( lc == 0 ) then
	    return PVP;
	end
	return LFG_TYPE_RAID;
end

-- calculations
local function CombatCaps_GetRatingModifiers()
	local level = UnitLevel("player");
	if ( level < 70 ) then
		return COMBATCAPS_RATINGS[60];
	elseif ( level < 80 ) then
		return COMBATCAPS_RATINGS[70];
	else
		return COMBATCAPS_RATINGS[level];
	end
end

local function CombatCaps_GetExpertiseRatingModifier()
	local mods = CombatCaps_GetRatingModifiers();
	return mods[CR_EXPERTISE];
end

local function CombatCaps_GetMissingExpertiseRating()
	local expRatingCoeff = CombatCaps_GetExpertiseRatingModifier();
	local requiredExpertise = BASE_ENEMY_DODGE_CHANCE[CombatCaps_Cap()] * 4; -- 0.25% per 1 expertise
	
	local expertise, offhandExpertise = GetExpertise();
	local currentSkillBonus = expertise - math.floor(GetCombatRatingBonus(CR_EXPERTISE));  -- required expetise points after skill
	local required = math.ceil((requiredExpertise - currentSkillBonus) * CombatCaps_GetExpertiseRatingModifier()); -- 780.7072
	
	local diff = required - GetCombatRating(CR_EXPERTISE);
	
	return diff * -1;
end

local function CombatCaps_GetMeeleHitRatingModifier()
	local mods = CombatCaps_GetRatingModifiers();
	return mods[CR_HIT_MELEE];
end

local function CombatCaps_GetMissingMeeleHitRating()
	local required = math.ceil( ( BASE_MISS_CHANCE_PHYSICAL[CombatCaps_Cap()] - GetHitModifier() ) * CombatCaps_GetMeeleHitRatingModifier() ); -- 961
	local diff = required - GetCombatRating(CR_HIT_MELEE); -- required minus current rating
	return diff * -1;
end

local function CombatCaps_GetSpellHitRatingModifier()
	local mods = CombatCaps_GetRatingModifiers();
	return mods[CR_HIT_SPELL];
end

local function CombatCaps_GetMissingSpellHitRating()
	local required = math.ceil( ( BASE_MISS_CHANCE_SPELL[CombatCaps_Cap()] - GetSpellHitModifier() ) * CombatCaps_GetSpellHitRatingModifier() );
	local diff = required - GetCombatRating(CR_HIT_SPELL); -- required minus current rating
	return diff * -1;
end

local function CombatCaps_GetRangedHitRatingModifier()
	local mods = CombatCaps_GetRatingModifiers();
	return mods[CR_HIT_MELEE];
end

local function CombatCaps_GetMissingRangedHitRating()
	local required = math.ceil( ( BASE_MISS_CHANCE_PHYSICAL[CombatCaps_Cap()] - GetHitModifier() ) * CombatCaps_GetRangedHitRatingModifier() ); -- 961
	local diff = required - GetCombatRating(CR_HIT_RANGED); -- required minus current rating
	return diff * -1;
end

local function CombatCaps_GetModeText()
	return format(L["CombatCaps is currently in %s mode."], CombatCaps_GetCalculationModeName());
end

local function CombatCaps_PrintMode()
	print(CombatCaps_GetModeText());
end

local function CombatCaps_PrintHelp()
	print(L["CombatCaps options are:"]);
	print(format("%s hero - %s", SLASH_COMBATCAPS1, format(L["enable calculation for %s caps"], LFG_TYPE_HEROIC_DUNGEON ) ) );
	print(format("%s raid - %s", SLASH_COMBATCAPS1, format(L["enable calculation for %s caps"], LFG_TYPE_RAID ) ) );
	print(format("%s pvp - %s", SLASH_COMBATCAPS1, format(L["enable calculation for %s caps"], PVP ) ) );
	CombatCaps_PrintMode();
end

-- hooks

local function CombatCaps_Expertise_OnEnter(statFrame)
	CombatCaps_AddTooltipLine(ITEM_MOD_EXPERTISE_RATING_SHORT, CombatCaps_GetMissingExpertiseRating());
end
hooksecurefunc("Expertise_OnEnter", CombatCaps_Expertise_OnEnter);

function CombatCaps_PaperDollFrame_SetExpertise(statFrame, unit)
	CombatCaps_AddStatText(_G[statFrame:GetName().."StatText"], CombatCaps_GetMissingExpertiseRating());
end
hooksecurefunc("PaperDollFrame_SetExpertise", CombatCaps_PaperDollFrame_SetExpertise);

local function CombatCaps_MeleeHitChance_OnEnter(statFrame)
	CombatCaps_AddTooltipLine(COMBAT_RATING_NAME6, CombatCaps_GetMissingMeeleHitRating());
end
hooksecurefunc("MeleeHitChance_OnEnter", CombatCaps_MeleeHitChance_OnEnter);

local function CombatCaps_PaperDollFrame_SetMeleeHitChance(statFrame, unit)
    CombatCaps_AddStatText( _G[statFrame:GetName().."StatText"], CombatCaps_GetMissingMeeleHitRating());
end
hooksecurefunc("PaperDollFrame_SetMeleeHitChance", CombatCaps_PaperDollFrame_SetMeleeHitChance);

local function CombatCaps_SpellHitChance_OnEnter(statFrame)
	CombatCaps_AddTooltipLine(COMBAT_RATING_NAME6, CombatCaps_GetMissingSpellHitRating());
end
hooksecurefunc("SpellHitChance_OnEnter", CombatCaps_SpellHitChance_OnEnter);

local function CombatCaps_PaperDollFrame_SetSpellHitChance(statFrame, unit)
	CombatCaps_AddStatText( _G[statFrame:GetName().."StatText"], CombatCaps_GetMissingSpellHitRating());
end
hooksecurefunc("PaperDollFrame_SetSpellHitChance", CombatCaps_PaperDollFrame_SetSpellHitChance);

local function CombatCaps_RangedHitChance_OnEnter(statFrame)
	CombatCaps_AddTooltipLine(COMBAT_RATING_NAME6, CombatCaps_GetMissingRangedHitRating());
end
hooksecurefunc("RangedHitChance_OnEnter", CombatCaps_RangedHitChance_OnEnter);

local function CombatCaps_PaperDollFrame_SetRangedHitChance(statFrame, unit)
    CombatCaps_AddStatText( _G[statFrame:GetName().."StatText"], CombatCaps_GetMissingRangedHitRating());
end
hooksecurefunc("PaperDollFrame_SetRangedHitChance", CombatCaps_PaperDollFrame_SetRangedHitChance);

-- display functions

function CombatCaps_AddStatText(textString, value)
	if ( value < 0 ) then
		textString:SetText(format(COMBATCAPS_FORMATSTRING, textString:GetText(), RED_FONT_COLOR_CODE, (value*-1), FONT_COLOR_CODE_CLOSE));
	elseif (value > 0) then
		textString:SetText(format(COMBATCAPS_FORMATSTRING, textString:GetText(), GREEN_FONT_COLOR_CODE, value, FONT_COLOR_CODE_CLOSE));
	end
end

function CombatCaps_AddTooltipLine(rating, value)
	GameTooltip:AddLine(" ");
	
	value = value * -1;

	if(value > 0) then
		GameTooltip:AddLine(format(L["You need %i more %s."], value, rating));
	elseif( value == 0 ) then
		GameTooltip:AddLine(format(L["Your %s matches perfectly."], rating));
	else
		GameTooltip:AddLine(format(L["You can reforge %i %s."], value * -1, rating));
	end
	
	GameTooltip:AddLine(CombatCaps_GetModeText());
	
	GameTooltip:Show();
end

function CombatCaps_PrintLines(...)
	local args = {...}
	for i=1, table.getn(args), 1 do
		DEFAULT_CHAT_FRAME:AddMessage(args[i], 0.0, 1.0, 0.25); -- Default green
	end
end

local CombarCaps_Frame = CreateFrame("FRAME");
CombarCaps_Frame:RegisterEvent("ADDON_LOADED"); -- Fired when saved variables are loaded
-- CombarCaps_Frame:RegisterEvent("PLAYER_LOGOUT"); -- Fired when about to log out

function CombarCaps_Frame:OnEvent(event, prefix, addonMessage, whisper, sender)
	if ( event == "ADDON_LOADED" and prefix == "CombatCaps" ) then
		if ( CombatCaps_CharacterConfig == nil or CombatCaps_CharacterConfig["Options"] == nil ) then
			CombatCaps_CharacterConfig = CombatCaps_Defaults;
		end
		CombatCaps_PrintMode();
	end
end

CombarCaps_Frame:SetScript("OnEvent", CombarCaps_Frame.OnEvent);

SLASH_COMBATCAPS1 = "/combatcaps";
function SlashCmdList.COMBATCAPS(msg)
	if ( msg == nil or msg == "" ) then
		CombatCaps_PrintHelp();
	elseif ( msg == "pvp" ) then
		CombatCaps_CharacterConfig["Options"]["LevelCap"] = 0;
		CombatCaps_PrintMode();
		PaperDollFrame_QueuedUpdate(_G["PaperDollFrame"]);
	elseif ( msg == "hero" ) then
		CombatCaps_CharacterConfig["Options"]["LevelCap"] = 2;
		CombatCaps_PrintMode();
		PaperDollFrame_QueuedUpdate(_G["PaperDollFrame"]);
	elseif ( msg == "raid" ) then
		CombatCaps_CharacterConfig["Options"]["LevelCap"] = 3;
		CombatCaps_PrintMode();
		PaperDollFrame_QueuedUpdate(_G["PaperDollFrame"]);
	else
		CombatCaps_PrintHelp();
	end
end